package service

import (
	"bufio"
	"errors"
	"fmt"
	"net/url"
	"strings"
	"time"

	"gitee.com/sansaniot/ssiot-core/httpmvc/service"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"

	"ssdevice/common/constant"
	cDto "ssdevice/common/dto"
	"ssdevice/common/enums"
	"ssdevice/common/utils"
	"ssdevice/internal/device/models"
	"ssdevice/internal/device/models/command"
	"ssdevice/internal/device/models/query"
)

type Product struct {
	devProductModel *models.SysDevProduct
	devSensorModel  *models.SysDevSensor
	devConfigModel  *models.SysDevConfig
	devFuncModel    *models.SysDevFunc
	devModel        *models.SysDev
	service.Service
}

func (d *Product) SetProductModel(m *models.SysDevProduct) *Product {
	d.devProductModel = m
	return d
}

func (d *Product) SetSensorModel(m *models.SysDevSensor) *Product {
	d.devSensorModel = m
	return d
}

func (d *Product) SetConfigModel(m *models.SysDevConfig) *Product {
	d.devConfigModel = m
	return d
}

func (d *Product) SetFuncModel(m *models.SysDevFunc) *Product {
	d.devFuncModel = m
	return d
}

func (d *Product) SetDevModel(m *models.SysDev) *Product {
	d.devModel = m
	return d
}

func (d *Product) SetAllModel() {
	d.devProductModel = &models.SysDevProduct{}
	d.devSensorModel = &models.SysDevSensor{}
	d.devConfigModel = &models.SysDevConfig{}
	d.devFuncModel = &models.SysDevFunc{}
	d.devModel = &models.SysDev{}
	d.devProductModel.Orm = d.Orm
	d.devSensorModel.Orm = d.Orm
	d.devConfigModel.Orm = d.Orm
	d.devFuncModel.Orm = d.Orm
	d.devModel.Orm = d.Orm
}

func generateProductConfig(product models.SysDevProduct, devConfig *models.SysDevConfig) *models.SysDevConfig {
	now := cDto.LocalTime(time.Now())
	configData := []byte(utils.If(product.DeviceType == enums.DevTypeOfDevice, constant.DevConfigTemplate, constant.GatewayConfigTemplate).(string))
	if devConfig != nil {
		configData = devConfig.Data
	}
	return &models.SysDevConfig{
		Id:           utils.SimpleUUID(),
		Data:         configData,
		Schema:       utils.MapToBytes(utils.GetObjectSchema(utils.BytesToMap(configData))["properties"].(map[string]interface{})),
		ProductModel: product.ProductModel,
		DevProductId: product.Id,
		CreateTime:   &now,
		UpdateTime:   &now,
	}
}

func generateProductFunc(product models.SysDevProduct, devFunc *models.SysDevFunc) *models.SysDevFunc {
	now := cDto.LocalTime(time.Now())
	funcData := []byte(utils.If(product.DeviceType == enums.DevTypeOfDevice, constant.DevFuncTemplate, constant.GatewayFuncTemplate).(string))
	if devFunc != nil {
		funcData = devFunc.Data
	}
	return &models.SysDevFunc{
		Id:           utils.SimpleUUID(),
		Data:         funcData,
		Schema:       utils.MapToBytes(utils.GetObjectSchema(utils.BytesToMap(funcData))["properties"].(map[string]interface{})),
		ProductModel: product.ProductModel,
		DevProductId: product.Id,
		CreateTime:   &now,
		UpdateTime:   &now,
	}
}

func generateProductSensor(product models.SysDevProduct, devSensors []models.SysDevSensor) []models.SysDevSensor {
	now := cDto.LocalTime(time.Now())
	ss := make([]models.SysDevSensor, 0)
	for _, sensor := range devSensors {
		ss = append(ss, models.SysDevSensor{
			Id:           utils.SimpleUUID(),
			ProductModel: product.ProductModel,
			Key:          sensor.Key,
			Label:        sensor.Label,
			Type:         sensor.Type,
			Index:        sensor.Index,
			Schema:       sensor.Schema,
			Config:       sensor.Config,
			Param:        sensor.Param,
			CreateTime:   &now,
			UpdateTime:   &now,
		})
	}
	return ss
}

// 添加产品
func (d *Product) AddProduct(req *command.ProductInsertReq) (err error, product models.SysDevProduct) {
	if len(req.UserId) > 0 && req.DeptId == constant.PublicGroupId && req.UserId != "1" {
		err = errors.New("没权限操作公共分组资源")
		return
	}
	if err = req.Generate(&product); err != nil {
		return
	}
	err = d.Orm.Transaction(func(tx *gorm.DB) error {
		// 事务orm
		d.devProductModel.Orm = tx
		d.devSensorModel.Orm = tx
		d.devConfigModel.Orm = tx
		d.devFuncModel.Orm = tx
		// 产品基本信息
		if err = d.devProductModel.AddProduct(&product); err != nil {
			return err
		}
		// 产品config
		addConfig := generateProductConfig(product, req.DevConfig)
		if err = d.devConfigModel.Add(addConfig); err != nil {
			return err
		}
		// 产品func
		addFunc := generateProductFunc(product, req.DevFunc)
		if err = d.devFuncModel.Add(addFunc); err != nil {
			return err
		}
		// 产品变量
		addSensors := generateProductSensor(product, req.DevSensors)
		if len(addSensors) > 0 {
			if err = d.devSensorModel.Add(&addSensors); err != nil {
				return err
			}
		}
		return nil
	})
	return
}

// 编辑产品
func (d *Product) EditProduct(req *command.ProductEditReq) (err error) {
	if err = d.devProductModel.FindOne(d.devProductModel, cDto.MakeCondition(query.SysProductQuery{Id: req.Id})); err != nil {
		return
	}
	if d.devProductModel.DeptId == constant.PublicGroupId && req.UserId != "1" {
		return errors.New("没权限操作公共分组资源")
	}
	return d.devProductModel.ModProduct(req.GenerateMap())
}

// 删除产品
func (d *Product) DeleteProduct(req *command.ProductDeleteReq) (err error) {
	if err = d.Orm.Transaction(func(tx *gorm.DB) error {
		// 事务orm
		d.devModel.Orm = tx
		d.devProductModel.Orm = tx
		d.devSensorModel.Orm = tx
		d.devConfigModel.Orm = tx
		d.devFuncModel.Orm = tx
		// 批量操作
		delIds := strings.Split(strings.ReplaceAll(req.Id, " ", ""), ",")
		var delModels []string
		if delModels, err = d.devProductModel.GetIdModels(delIds, req.UserId); err != nil {
			return err
		}
		// 置空关联设备的productModel字段
		if err = d.devProductModel.SetRelateDevCodeEmpty(delModels); err != nil {
			return err
		}
		// 产品信息
		if err = d.devProductModel.Delete(d.devProductModel, cDto.MakeCondition(query.SysProductQuery{IdIn: delIds})); err != nil {
			return err
		}
		// 产品变量
		if err = d.devSensorModel.Delete(d.devSensorModel, cDto.MakeCondition(query.SysDevSensorQuery{ProductModelIn: delModels})); err != nil {
			return err
		}
		// 产品config
		if err = d.devConfigModel.Delete(d.devConfigModel, cDto.MakeCondition(query.SysDevConfigQuery{ProductIdIn: delIds})); err != nil {
			return err
		}
		// 产品func
		if err = d.devFuncModel.Delete(d.devFuncModel, cDto.MakeCondition(query.SysDevFuncQuery{ProductIdIn: delIds})); err != nil {
			return err
		}
		return nil
	}); err != nil {
		return
	}
	return
}

// 产品复制
func (d *Product) ProductCopy(req *command.ProductCopyReq) (one models.SysDevProduct, err error) {
	product := models.SysDevProduct{}
	if err = d.devProductModel.FindOne(&product, cDto.MakeCondition(query.SysProductQuery{Id: req.Id})); err != nil {
		return
	}
	if product.DeptId == constant.PublicGroupId && req.UserId != "1" {
		err = errors.New("没权限操作公共分组资源")
		return
	}
	productInsert := command.ProductInsertReq{}
	req.GenerateProduct(&product, &productInsert)
	// 查询产品变量
	sensors := make([]models.SysDevSensor, 0)
	if err = d.devSensorModel.FindList(&sensors, cDto.MakeCondition(query.SysDevSensorQuery{ProductModel: product.ProductModel})); err != nil {
		return
	}
	// 查询产品config
	if err = d.devConfigModel.FindOne(d.devConfigModel, cDto.MakeCondition(query.SysDevConfigQuery{ProductId: product.Id})); err != nil {
		return
	}
	// 查询产品func
	if err = d.devFuncModel.FindOne(d.devFuncModel, cDto.MakeCondition(query.SysDevFuncQuery{ProductId: product.Id})); err != nil {
		return
	}
	productInsert.DevSensors = sensors
	productInsert.DevConfig = d.devConfigModel
	productInsert.DevFunc = d.devFuncModel
	// 添加产品
	sp := &Product{}
	sp.Orm = d.Orm
	sp.SetAllModel()
	err, one = sp.AddProduct(&productInsert)
	return
}

// 产品设备同步更新
func (d *Product) ProductDevSync(req *command.ProductDevSyncReq) (err error) {
	if err = d.devProductModel.FindOne(d.devProductModel, cDto.MakeCondition(query.SysProductQuery{ProductModel: req.ProductModel})); err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			err = nil
		}
		return
	}
	if d.devProductModel.DeptId == constant.PublicGroupId && req.UserId != "1" {
		err = errors.New("没权限操作公共分组资源")
		return
	}
	// 该产品的设备s
	devList := make([]models.SysDev, 0)
	if err = d.devModel.FindList(&devList, cDto.MakeCondition(query.SysDevQuery{Sn: req.Sn, ProductModel: d.devProductModel.ProductModel})); err != nil {
		return
	}
	if len(devList) == 0 {
		return
	}
	snList := make([]string, 0)
	snDevMap := make(map[string]models.SysDev)
	for _, dev := range devList {
		snList = append(snList, dev.Sn)
		snDevMap[dev.Sn] = dev
	}
	err = d.Orm.Transaction(func(tx *gorm.DB) error {
		// 设备有这个表
		systemM := &models.SysDevSystem{}
		systemM.Orm = tx
		d.devSensorModel.Orm = tx
		d.devConfigModel.Orm = tx
		d.devFuncModel.Orm = tx
		// 设备变量
		if err = d.devSensorModel.Delete(d.devSensorModel, cDto.MakeCondition(query.SysDevSensorQuery{SnIn: snList})); err != nil {
			return err
		}
		// 设备profile.config
		if err = d.devConfigModel.Delete(d.devConfigModel, cDto.MakeCondition(query.SysDevConfigQuery{SnIn: snList})); err != nil {
			return err
		}
		if err = d.devFuncModel.Delete(d.devFuncModel, cDto.MakeCondition(query.SysDevFuncQuery{SnIn: snList})); err != nil {
			return err
		}
		// 设备profile.system
		if err = systemM.Delete(systemM, cDto.MakeCondition(query.SysDevSystemQuery{SnIn: snList})); err != nil {
			return err
		}

		// 根据产品生成
		psensors := make([]models.SysDevSensor, 0)
		if err = d.devSensorModel.FindList(&psensors, cDto.MakeCondition(query.SysDevSensorQuery{ProductModel: d.devProductModel.ProductModel})); err != nil {
			return err
		}
		if err = d.devConfigModel.FindOne(d.devConfigModel, cDto.MakeCondition(query.SysDevConfigQuery{ProductId: d.devProductModel.Id})); err != nil {
			return err
		}
		if err = d.devFuncModel.FindOne(d.devFuncModel, cDto.MakeCondition(query.SysDevFuncQuery{ProductId: d.devProductModel.Id})); err != nil {
			return err
		}
		configs := make([]models.SysDevConfig, 0)
		funcs := make([]models.SysDevFunc, 0)
		sensors := make([]models.SysDevSensor, 0)
		systems := make([]models.SysDevSystem, 0)
		for _, sn := range snList {
			dev := snDevMap[sn]
			// 特性
			devConfig, devSensors, devSystem, devFunc := generateProfile(tx, &dev, *d.devConfigModel, psensors, *d.devFuncModel, &command.DeviceInsertReq{})
			configs = append(configs, *devConfig)
			funcs = append(funcs, *devFunc)
			sensors = append(sensors, devSensors...)
			systems = append(systems, *devSystem)
		}
		// 设备sensors
		if len(sensors) > 0 {
			if err = d.devSensorModel.Add(&sensors); err != nil {
				return err
			}
		}
		// 设备profile.config
		if err = d.devConfigModel.Add(&configs); err != nil {
			return err
		}
		if err = d.devFuncModel.Add(&funcs); err != nil {
			return err
		}
		// 设备profile.system
		if err = systemM.Add(&systems); err != nil {
			return err
		}
		return nil
	})
	return
}

// 导出产品
func (d *Product) ExportProduct(c *gin.Context, req *query.SysProductQuery) (err error) {
	// 获取产品相关信息
	var content []byte
	if content, err = getProductMeta(d.Orm, req); err != nil {
		return
	}
	//// 响应文件流
	fileName := fmt.Sprintf("产品元数据.json")
	c.Header("Content-Disposition", fmt.Sprintf("attachment; filename*=utf-8''%s", url.QueryEscape(fileName)))
	c.Header("File-Name", url.QueryEscape(fileName))
	c.Header("Content-Type", "application/octet-stream;charset=UTF-8")
	_, err = c.Writer.Write(content)
	return
}

// 导入产品
func (d *Product) ImportProduct(c *gin.Context, req *query.SysProductQuery) error {
	// 获取文件对象
	file, header, err := c.Request.FormFile("file")
	if err != nil {
		return err
	}
	if file == nil || !strings.HasSuffix(header.Filename, ".json") {
		return errors.New("非法文件，请使用正确的文件导入")
	}
	defer file.Close()
	var content string
	scanner := bufio.NewScanner(file)
	// 逐行读取文件内容
	for scanner.Scan() {
		content += scanner.Text()
	}
	if err = scanner.Err(); err != nil {
		return err
	}
	contentMapArr := utils.BytesToMapArr([]byte(content))
	// 执行导入
	return importProductMeta(d.Orm, contentMapArr)
}

// 配置整体操作
func (d *Product) UpsertEntireConfig(req *command.ProductDeviceConfigReq) (err error) {
	sysProduct := models.SysDevProduct{}
	if err = d.devProductModel.FindOne(&sysProduct, cDto.MakeCondition(query.SysProductQuery{Id: req.Id})); err != nil {
		return
	}
	if sysProduct.DeptId == constant.PublicGroupId && req.UserId != "1" {
		return errors.New("没权限操作公共分组资源")
	}
	// 整体替换网关的profile.config
	path := "{config}"
	schemaPath := "{config,properties}"
	if len(req.NodePath) > 0 {
		path = "{" + strings.ReplaceAll(req.NodePath, ".", ",") + "}"
		schemaPath = "{" + strings.ReplaceAll(req.NodePath, ".", ",properties,") + "}"
	}
	dataStr := string(utils.MapToBytes(req.Data))
	schemaStr := string(utils.MapToBytes(req.Schema))
	sql := "update sys_dev_config set data = jsonb_set(data::jsonb, '" + path + "', '" + dataStr + "' :: jsonb ), schema = jsonb_set(schema::jsonb, '" + schemaPath + "', '" + schemaStr + "' :: jsonb )  where dev_product_id = '" + sysProduct.Id + "' "
	if err = d.Orm.Raw(sql).Scan(d.devConfigModel).Error; err != nil {
		return err
	}
	return
}
